﻿using System;
using System.Buffers.Binary;

namespace WholeSlideImageLibrary.Formats.OptimizedPyramidTiledImage
{
#pragma warning disable CA1815 // 重写值类型上的 Equals 和相等运算符
    public readonly struct FileMainBlock
#pragma warning restore CA1815 // 重写值类型上的 Equals 和相等运算符
    {
        public FileMainBlock(int width, int height, byte levelCount, ushort tileSize, string format, double micronsPerPixelX, double micronsPerPixelY, long levelCollectionBlockPointer, long metadataBlockPointer, long associatedImagesBlockPointer)
        {
            Width = width;
            Height = height;
            LevelCount = levelCount;
            TileSize = tileSize;
            Format = format;
            MicronsPerPixelX = micronsPerPixelX;
            MicronsPerPixelY = micronsPerPixelY;
            LevelCollectionBlockPointer = levelCollectionBlockPointer;
            MetadataBlockPointer = metadataBlockPointer;
            AssociatedImagesBlockPointer = associatedImagesBlockPointer;
        }

        public FileMainBlock(int width, int height, byte levelCount, ushort tileSize, string format, double micronsPerPixelX, double micronsPerPixelY, long levelCollectionBlockPointer, long metadataBlockPointer, long associatedImagesBlockPointer, long totalFileSize)
        {
            Width = width;
            Height = height;
            LevelCount = levelCount;
            TileSize = tileSize;
            Format = format;
            MicronsPerPixelX = micronsPerPixelX;
            MicronsPerPixelY = micronsPerPixelY;
            LevelCollectionBlockPointer = levelCollectionBlockPointer < 0 ? totalFileSize + levelCollectionBlockPointer : levelCollectionBlockPointer;
            MetadataBlockPointer = metadataBlockPointer < 0 ? totalFileSize + metadataBlockPointer : metadataBlockPointer;
            AssociatedImagesBlockPointer = associatedImagesBlockPointer < 0 ? totalFileSize + associatedImagesBlockPointer : associatedImagesBlockPointer;
        }

        public const int MarshalSize = 56;

        public int Width { get; }
        public int Height { get; }
        public byte LevelCount { get; }
        public ushort TileSize { get; }
        public string Format { get; }
        public double MicronsPerPixelX { get; }
        public double MicronsPerPixelY { get; }
        public long LevelCollectionBlockPointer { get; }
        public long MetadataBlockPointer { get; }
        public long AssociatedImagesBlockPointer { get; }

        public static bool TryParse(ReadOnlySpan<byte> buffer, long totalFileLength, out FileMainBlock mainBlock)
        {
            if (buffer.Length < MarshalSize)
            {
                mainBlock = default;
                return false;
            }

            // Overlap 目前只支持0
            if (buffer[11] != 0)
            {
                mainBlock = default;
                return false;
            }

            string format = EncodingHelper.DecodeUtf8(buffer.Slice(12, 4));
            if (!"jpeg".Equals(format, StringComparison.Ordinal))
            {
                mainBlock = default;
                return false;
            }

            mainBlock = new FileMainBlock(
                width: BinaryPrimitives.ReadInt32LittleEndian(buffer),
                height: BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(4)),
                levelCount: buffer[8],
                tileSize: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(9)),
                format: format,
                micronsPerPixelX: BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(16))),
                micronsPerPixelY: BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(24))),
                levelCollectionBlockPointer: BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(32)),
                metadataBlockPointer: BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(40)),
                associatedImagesBlockPointer: BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(48)),
                totalFileSize: totalFileLength
                );
            return true;
        }

        public bool TryWriteTo(Span<byte> destination)
        {
            if (destination.Length < MarshalSize)
            {
                return false;
            }

            string format = Format;
            if (format is null || format.Length != 4)
            {
                return false;
            }

            BinaryPrimitives.WriteInt32LittleEndian(destination, Width);
            BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4), Height);
            destination[8] = LevelCount;
            BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(9), TileSize);
            destination[11] = 0; // Overlap
            EncodingHelper.EncodeUtf8(format, destination.Slice(12, 4));
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(16), BitConverter.DoubleToInt64Bits(MicronsPerPixelX));
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(24), BitConverter.DoubleToInt64Bits(MicronsPerPixelY));
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(32), LevelCollectionBlockPointer);
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(40), MetadataBlockPointer);
            BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(48), AssociatedImagesBlockPointer);

            return true;
        }

    }
}
